extends Node2D

## 地图场景
##
## 负责展示DataMap中的相关数据，以及处理用户的操作

# 地图数据，由世界创建好，并注入当前地图场景中
var data_map: DataMap

var lock_monster_scene

const EFFECT_SKILLS = [
	"skill_000100",
	"skill_000101",
	"skill_000103"
]

# 怪物击杀开始时间 key:怪物id value:开始时间
var boss_start_time: Dictionary = {}

# 黑化怪物场景 key:monster_unique_id，value: Monster
# 从world中注入
var black_monster_scene_dic: Dictionary = {}


signal portal_updated(portal: DataPortal,map_scene: Node2D)

signal invoked_portal(portal: DataPortal, map_scene: Node2D)

signal attack_target_changed(attack_target)

signal map_drop_showed(data_map: DataMap)

signal drop_dic_showed(data_map: DataMap)

signal endless_exit()

signal black_monster_finish(monster_scene: Monster)

signal black_monster_dead(monster_scene: Monster)


# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	# 确保地图数据已经加载
	assert(data_map, "地图数据未加载")
	# 地图名称
	$CanvasLayer/Name.text = data_map.name

	# 加载地图传送点
	for portal in data_map.data_portals.values():
		_add_portal(portal)
	# 加载地图NPC
	for npc in data_map.data_npcs.values():
		_add_NPC(npc)
	
	# 加载无尽之塔
	if data_map.id == "map_000199":
		var endless_control = SingletonGameScenePre.EndlessControlScene.instantiate()
		$CanvasLayer.add_child(endless_control)
		# 注入数据
		endless_control.init_data(data_map)
		# 监听无尽之塔退出事件
		endless_control.endless_exit.connect(_on_endless_exit)
		# 监听无尽之塔领取奖励事件
		endless_control.get_endless_reward.connect(_on_get_endless_reward)

	# 监听
	data_map.first_player_entered.connect(_on_first_player_entered)
	data_map.monster_added.connect(_add_monster)
	data_map.npc_added.connect(_on_npc_added)
	data_map.drop_thing_dropped.connect(_on_drop_thing_dropped)
	data_map.drop_thing_picked.connect(_on_drop_thing_picked)
	data_map.boss_combat_started.connect(_on_boss_combat_started)
	data_map.boss_killed.connect(_on_boss_killed)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	# 更新击杀记录
	_update_boss_kill_record()

	# 如果有怪物有自动锁定丢失目标，则重新锁定
	_process_auto_lock_player()

	# 处理黑化怪物的目标锁定
	_process_black_monster()


func _process_black_monster():
	var all_monsters = $CanvasLayer/GameZone/Monsters.get_children()
	for monster_scene in all_monsters:
		if monster_scene.attack_target == null \
			and monster_scene.data_monster \
			and monster_scene.data_monster.is_black_monster \
			and not monster_scene.data_monster.is_dead:
			
			var closest_distance = INF
			var nearest_target = null
			
			for candidate in all_monsters:
				if not candidate.data_monster: continue
				if candidate.data_monster.is_black_monster: continue  # 跳过黑色怪物
				if candidate.data_monster.is_dead: continue  # 跳过死亡怪物
				
				var distance = monster_scene.global_position.distance_to(candidate.global_position)
				if distance < closest_distance:
					closest_distance = distance
					nearest_target = candidate
			
			if nearest_target:
				monster_scene.set_attack_target(nearest_target)


func _process_auto_lock_player():
	if not data_map.get_player():
		return

	for monster_scene in $CanvasLayer/GameZone/Monsters.get_children():
		if monster_scene.attack_target == null \
			and monster_scene.data_monster \
			and monster_scene.data_monster.auto_lock_player \
			and not monster_scene.data_monster.is_black_monster:
			var player_scene = $CanvasLayer/GameZone/Players.get_node(data_map.get_player().player_id)
			monster_scene.set_attack_target(player_scene)


func _update_boss_kill_record():
	for start_time in boss_start_time.values():
		var hurt_time = Time.get_ticks_msec() - start_time
		$CanvasLayer/BossStatus/CostTime.text = str(hurt_time / 1000.0)


# 处理玩家嘲讽怪物
func _process_mock_monster(player_scene: Control,count: int):
	var invoked_count = 0
	for monster_scene in $CanvasLayer/GameZone/Monsters.get_children():
		if monster_scene.data_monster.is_dead:
			continue
		if monster_scene.attack_target != null:
			continue
		monster_scene.set_attack_target(player_scene)
		invoked_count += 1
		if invoked_count >= count:
			break


func add_all_player_names(_names: Array):
	var scene = SingletonGameScenePre.AllPlayersScene.instantiate()
	scene.show_names(_names)
	scene.position = Vector2(10,100)
	$CanvasLayer.add_child(scene)


# 添加黑化怪物的场景
func add_black_monster(monster_scene: Monster):
	# 加入场景
	$CanvasLayer/GameZone/Monsters.add_child(monster_scene)
	# 监听点击事件
	monster_scene.pressed.connect(_on_monster_pressed.bind(monster_scene))
	# 监听怪物技能执行
	monster_scene.data_monster.skill_executed.connect(_on_monster_skill_executed)
	# 监听怪物重置事件
	monster_scene.data_monster.monster_reseted.connect(_on_monster_reseted)


# 添加怪物
func _add_monster(monster: DataMonster,_position: Vector2):
	var monster_scene: Monster = null
	if black_monster_scene_dic.has(monster.monster_unique_id):
		monster_scene = black_monster_scene_dic[monster.monster_unique_id]
	else:
		# 普通怪物场景
		var monster_scene_pre = SingletonGameScenePre.MonsterScene
		# boss怪物场景
		if monster.type == "boss":
			monster_scene_pre = SingletonGameScenePre.MonsterBossScene
		# 初始化怪物场景
		monster_scene = monster_scene_pre.instantiate()
		monster_scene.data_monster = monster
		monster_scene.name = monster.monster_unique_id
		# 设置怪物位置
		# 获取地图场景的尺寸
		var size = $CanvasLayer/GameZone/Monsters.size
		if _position != Vector2.INF:
			# 如果怪物刷新点位置不为空，则设置怪物位置
			monster_scene.position = Vector2(_position.x * size.x, _position.y * size.y)
		else:
			# 如果怪物刷新点位置为空，则随机一个位置
			monster_scene.position = Vector2(randf() * size.x, randf() * size.y)
		# 如果玩家不在地图，则关闭怪物的碰撞检测(防止技能穿图)
		if data_map.data_player == null:
			monster_scene.close_monitorable()
	# 加入场景
	$CanvasLayer/GameZone/Monsters.add_child(monster_scene)
	# 监听点击事件
	monster_scene.pressed.connect(_on_monster_pressed.bind(monster_scene))
	# 监听黑化怪物完成事件
	monster_scene.black_monster_finish.connect(_on_black_monster_finish)
	# 监听怪物技能执行
	monster.skill_executed.connect(_on_monster_skill_executed)
	# 监听怪物重置事件
	monster.monster_reseted.connect(_on_monster_reseted)
	# 监听怪物死亡
	monster.role_dead.connect(_on_monster_dead)

	# 如果刷新的是boss，则展示击杀信息
	if monster.is_boss() and not monster.is_black_monster:
		$CanvasLayer/BossStatus.show()
		# 展示击杀记录
		if SingletonGame.boss_kill_time.has(monster.monster_id):
			$CanvasLayer/BossStatus/RecordTime.text = str(SingletonGame.boss_kill_time[monster.monster_id]/1000.0)
		else:
			$CanvasLayer/BossStatus/RecordTime.text = "-"
		# 展示击杀时间
		$CanvasLayer/BossStatus/CostTime.text = "-"
	# 如果怪物自动锁定玩家，则设置攻击目标
	if monster.auto_lock_player and data_map.get_player():
		var player_scene = $CanvasLayer/GameZone/Players.get_node(data_map.get_player().player_id)
		monster_scene.set_attack_target(player_scene)


func _on_monster_reseted(data_monster: DataMonster):
	# 如果是boss，归零击杀时间
	if data_monster.is_boss():
		boss_start_time.clear()
		$CanvasLayer/BossStatus/CostTime.text = "-"


func _on_black_monster_finish(monster_scene: Monster):
	# 黑化完成复活后，重新建立信号链接
	monster_scene.data_monster.role_dead.connect(_on_monster_dead)
	monster_scene.data_monster.skill_executed.connect(_on_monster_skill_executed)
	# 发出黑化怪物完成信号
	black_monster_finish.emit(monster_scene)


func _on_monster_dead(data_monster: DataMonster):
	# 断开信号
	data_monster.role_dead.disconnect(_on_monster_dead)
	data_monster.skill_executed.disconnect(_on_monster_skill_executed)
	data_monster.monster_reseted.disconnect(_on_monster_reseted)
	var monster_scene = $CanvasLayer/GameZone/Monsters.get_node(data_monster.monster_unique_id)
	if monster_scene:
		monster_scene.pressed.disconnect(_on_monster_pressed)
		if data_monster.is_black_monster:
			monster_scene.black_monster_finish.disconnect(_on_black_monster_finish)
			black_monster_dead.emit(monster_scene)


func _on_monster_skill_executed(data_monster: DataMonster, skill: DataBaseSkill):
	# 获取目标和自身的方向向量
	var monster_scene = $CanvasLayer/GameZone/Monsters.get_node(data_monster.monster_unique_id)
	if not monster_scene:
		return
	var target_scene = monster_scene.attack_target
	if target_scene == null:
		return
	var direction = target_scene.global_position - monster_scene.global_position
	# 获取目标和自身距离
	var distance = direction.length()
	# 标准化方向向量
	direction = direction.normalized()
	# 如果距离大于技能半径
	if distance <= skill.radius:
		# 怪物场景添加执行动画
		monster_scene.add_skill_execute_effect(skill,direction)
		# 技能执行
		var target_role: DataRole = null
		if target_scene is Player:
			target_role = target_scene.data_player
		elif target_scene is Monster:
			target_role = target_scene.data_monster
		if target_role:
			data_map.on_monster_skill_executed(data_monster, target_role, skill, direction)

		# 如果怪物没有死亡，则设定攻击目标
		if target_scene is Monster and not target_scene.data_monster.is_dead:
			target_scene.set_attack_target(monster_scene)


func _add_portal(portal: DataPortal):
	if portal.disabled:
		return
	
	var portal_scene = SingletonGameScenePre.PortalScene.instantiate()
	portal_scene.name = portal.portal_id
	portal_scene.text = portal.target_map_name
	portal_scene.position = portal.position
	portal_scene.pressed.connect(_on_portal_button_pressed.bind(portal))
	$CanvasLayer/GameZone/Portals.add_child(portal_scene)


## 更新传送点名称
func update_portal_name(portal: DataPortal,is_explored: bool):
	var portal_button = $CanvasLayer/GameZone/Portals.get_node(portal.portal_id)
	if portal_button != null:
		if is_explored:
			portal_button.text = portal.target_map_name
		else:
			portal_button.text = "???"
	

func _add_NPC(data_npc: DataNPC):
	var npc_scene = SingletonGameScenePre.NPCScene.instantiate()
	npc_scene.set_data(data_npc)
	npc_scene.position = data_npc.position
	$CanvasLayer/GameZone/Npc.add_child(npc_scene)


func _on_portal_button_pressed(portal: DataPortal):
	# print("_on_portal_button_pressed:",portal.portal_id)
	invoked_portal.emit(portal, self)


# 首个玩家进入地图，开始刷新怪物
func _on_first_player_entered():
	print("第一个玩家进入地图")
	if data_map.has_monster_refresh_pos():
		if $CloseMonsRefreshTimer.is_stopped():
			$MonsterRefreshTimer.start()
		else:
			$CloseMonsRefreshTimer.stop()


func _on_monster_refresh_timer_timeout() -> void:
	# print('_on_monster_refresh_timer_timeout')
	data_map.refresh_monsters()
	# 判断当前地图是否有正常怪物
	var has_monster = false
	for _data_monster in data_map.data_monsters.values():
		if not _data_monster.is_black_monster:
			has_monster = true
			break
	# 如果目前还无怪物，则遍历刷新点信息，更新刷新时间
	if not has_monster:
		for refresh_pos in data_map.map_monster_refresh_pos:
			for refresh_info in refresh_pos.monster_refresh_list:
				# 怪物刷新CD开始时间
				var start_time = data_map.monster_refresh_time_dic[refresh_info.monster_id]
				# 怪物刷新CD时间
				var cd_time = (refresh_info.refresh_interval / float(SingletonGame.speed))
				# 剩余时间
				var left_time = cd_time - (Time.get_unix_time_from_system() - start_time)
				$CanvasLayer/RefreshTime.show()
				$CanvasLayer/RefreshTime/RestTime.text = str(int(left_time))
				# 当前默认只有BOSS有刷新时间，只展示第一个
				return
	else:
		$CanvasLayer/RefreshTime.hide()


func _on_close_mons_refresh_timer_timeout() -> void:
	#print('_on_close_mons_refresh_timer_timeout')
	$MonsterRefreshTimer.stop()
	$CanvasLayer/RefreshTime.hide()


func _on_monster_pressed(monster_scene) -> void:
	if monster_scene.data_monster and monster_scene.data_monster.is_black_monster:
		return
	#print("_on_monster_pressed:", monster_scene.data_monster.monster_id)
	var data_player = data_map.get_player()
	if data_player == null:
		return
	var player_scene = $CanvasLayer/GameZone/Players.get_node(data_player.player_id)
	if monster_scene.is_selected:
		player_scene.set_attack_target(null)
		#monster_scene.unselect()
	else:
		player_scene.set_attack_target(monster_scene)


func _on_money_dropped(data_money: DataMoney, target: DataRole,offset_index:int):
	# 生成金币场景
	var money_scene = SingletonGameScenePre.MoneyScene.instantiate()
	money_scene.set_money(data_money)
	# 确认目标位置
	var target_pos = _get_data_role_position(target)
	money_scene.position = target_pos
	money_scene.x_offset = _offset_index_to_x(offset_index)
	
	$CanvasLayer/GameZone/DropThingControl.add_child(money_scene)


func _on_drop_thing_dropped(drop_thing: DataBagItem, target: DataRole,offset_index:int):
	# print("_on_drop_thing_dropped:", drop_thing.id)
	# 生成掉落物场景
	if drop_thing is DataMoney:
		_on_money_dropped(drop_thing, target,offset_index)
	else:
		var drop_thing_scene = SingletonGameScenePre.DropThingScene.instantiate()
		drop_thing_scene.set_item(drop_thing)
		# 确认目标位置
		var target_pos = _get_data_role_position(target)
		drop_thing_scene.position = target_pos
		drop_thing_scene.x_offset = _offset_index_to_x(offset_index)

		$CanvasLayer/GameZone/DropThingControl.add_child(drop_thing_scene)


func _get_data_role_position(data_role: DataRole) -> Vector2:
	if data_role is DataPlayer:
		var player_scene = $CanvasLayer/GameZone/Players.get_node(data_role.player_id)
		return player_scene.global_position
	elif data_role is DataMonster:
		var monster_scene = $CanvasLayer/GameZone/Monsters.get_node(data_role.monster_unique_id)
		return monster_scene.global_position
	else:
		return Vector2.ZERO


func _offset_index_to_x(index)-> int:
	var offset_unit = 26
	if index == 0:
		return 0
	elif index % 2 == 0:
		return -int(index / 2) * offset_unit
	else:
		return int((index+1)/2) * offset_unit


func _on_drop_thing_picked(_data_map: DataMap, drop_thing: DataBagItem):
	# 查找到对应的掉落物
	var drop_thing_scene = $CanvasLayer/GameZone/DropThingControl.get_node(drop_thing.uuid)
	# 查找到玩家
	var player_scene = $CanvasLayer/GameZone/Players.get_node(_data_map.get_player().player_id)
	# 设置拾取目标
	drop_thing_scene.set_pick_target(player_scene.global_position)


func _on_boss_combat_started(_data_map: DataMap, data_monster: DataMonster):
	boss_start_time[data_monster.monster_unique_id] = Time.get_ticks_msec()


func _on_boss_killed(_data_map: DataMap, data_monster: DataMonster):
	if boss_start_time.has(data_monster.monster_unique_id):
		var hurt_time = Time.get_ticks_msec() - boss_start_time[data_monster.monster_unique_id]
		boss_start_time.erase(data_monster.monster_unique_id)
	
		# 更新击杀时间
		$CanvasLayer/BossStatus/CostTime.text = str(hurt_time/1000.0)
		# 更新击杀记录
		if not SingletonGame.boss_kill_time.has(data_monster.monster_id) \
			or hurt_time < SingletonGame.boss_kill_time[data_monster.monster_id]:
			SingletonGame.boss_kill_time[data_monster.monster_id] = hurt_time
			$CanvasLayer/BossStatus/RecordTime.text = str(hurt_time/1000.0)


func _on_npc_added(data_npc: DataNPC):
	# print("_on_npc_added:",data_npc.npc_id)
	_add_NPC(data_npc)


func add_player_scene(data_player: DataPlayer,player_scene: Control):
	print('player_added:', data_player.player_id)
	# 获取地图场景的尺寸
	var size = $CanvasLayer/GameZone/Players.size
	# 设置玩家位置
	player_scene.position = Vector2(0.5 * size.x, 0.5 * size.y)
	# 监听寻找攻击目标
	player_scene.find_target_started.connect(_on_find_target_started)
	player_scene.find_far_target_started.connect(_on_find_far_target_started)
	# 监听攻击目标变换
	player_scene.attack_target_changed.connect(_on_attack_target_changed)
	# 监听玩家的技能执行
	data_player.skill_executed.connect(_on_skill_executed)
	# 监听玩家嘲讽怪物
	player_scene.mock_monster_invoked.connect(_on_mock_monster_invoked)
	# 监听战争践踏
	player_scene.war_stomp_triggered.connect(_on_war_stomp_triggered)
	# 监听玩家场景针对指定怪物的技能触发
	player_scene.skill_on_target_triggered.connect(_on_skill_on_target_triggered)

	# 加入场景
	$CanvasLayer/GameZone/Players.add_child(player_scene)

	# 更新传送点信息，由于传送点的展示需要依赖其他地图的信息，因此将事件交给上层处理
	for portal in data_map.data_portals.values():
		portal_updated.emit(portal,self)

	scene_show()
	
	# 遍历所有怪物场景
	if data_map:
		for child in $CanvasLayer/GameZone/Monsters.get_children():
			# 开启碰撞体检测
			child.start_monitorable()
		
	# 日落村彩蛋播放
	if data_map.id == "map_000001":
		var all_players_scene = get_node("CanvasLayer/AllPlayers")
		if all_players_scene:
			all_players_scene.start_ani()


## 添加分身场景
func add_player_clone_scene(_player_clone: DataPlayer,player_scene: Control):
	print('player_added:', _player_clone.player_id)
	# 获取地图场景的尺寸
	var size = $CanvasLayer/GameZone/Players.size
	# 获取玩家数量
	var player_count = $CanvasLayer/GameZone/Players.get_child_count()
	var offset_pos = int(player_count / 2.0)
	if player_count % 2 == 1:
		# 设置玩家位置
		player_scene.position = Vector2((0.3 - offset_pos * 0.2) * size.x,(0.3 - offset_pos * 0.2) * size.y)
	else :
		player_scene.position = Vector2((0.5 + offset_pos * 0.2) * size.x,(0.5 - offset_pos * 0.2) * size.y)
	# 监听寻找攻击目标
	player_scene.find_target_started.connect(_on_find_target_started)
	player_scene.find_far_target_started.connect(_on_find_far_target_started)
	# 监听攻击目标变换
	player_scene.attack_target_changed.connect(_on_attack_target_changed)
	# 监听玩家的技能执行
	_player_clone.skill_executed.connect(_on_skill_executed)
	# 监听玩家嘲讽怪物
	player_scene.mock_monster_invoked.connect(_on_mock_monster_invoked)

	# 加入场景
	$CanvasLayer/GameZone/Players.add_child(player_scene)



func remove_player_scene(data_player: DataPlayer):
	print('remove_player:', data_player.player_id)
	# 根据玩家id删除场景
	var player_scene = $CanvasLayer/GameZone/Players.get_node(data_player.player_id)
	$CanvasLayer/GameZone/Players.remove_child(player_scene)
	# 重置玩家场景的状态
	player_scene.reset()
	
	# 遍历所有怪物场景
	if data_map:
		for child in $CanvasLayer/GameZone/Monsters.get_children():
			# 玩家离开后，清理所有攻击目标
			child.set_attack_target(null)
			# 关闭碰撞体检测（会穿图）
			child.close_monitorable()
			# 黑化怪物删除场景。
			if child.data_monster.is_black_monster:
				$CanvasLayer/GameZone/Monsters.remove_child(child)
				# 黑化怪物场景删除后，add_monster添加的相关信号解除。
				child.pressed.disconnect(_on_monster_pressed)
				child.black_monster_finish.disconnect(_on_black_monster_finish)
				child.data_monster.skill_executed.disconnect(_on_monster_skill_executed)
				child.data_monster.monster_reseted.disconnect(_on_monster_reseted)
				child.data_monster.role_dead.disconnect(_on_monster_dead)

	# 解除监听
	player_scene.find_target_started.disconnect(_on_find_target_started)
	player_scene.find_far_target_started.disconnect(_on_find_far_target_started)
	player_scene.attack_target_changed.disconnect(_on_attack_target_changed)
	data_player.skill_executed.disconnect(_on_skill_executed)
	player_scene.mock_monster_invoked.disconnect(_on_mock_monster_invoked)
	player_scene.war_stomp_triggered.disconnect(_on_war_stomp_triggered)
	player_scene.skill_on_target_triggered.disconnect(_on_skill_on_target_triggered)

	# 如果地图没有玩家了，延时关闭怪物刷新
	$CloseMonsRefreshTimer.start()

	# 地图隐藏
	scene_hide()


## 删除分身场景
func remove_player_clone_scene(data_player: DataPlayer):
	print('remove_player:', data_player.player_id)
	# 根据玩家id删除场景
	var player_scene = $CanvasLayer/GameZone/Players.get_node(data_player.player_id)
	$CanvasLayer/GameZone/Players.remove_child(player_scene)
	# 移除攻击目标
	player_scene.attack_target = null
	
	# 解除监听
	player_scene.find_target_started.disconnect(_on_find_target_started)
	player_scene.find_far_target_started.disconnect(_on_find_far_target_started)
	player_scene.attack_target_changed.disconnect(_on_attack_target_changed)
	data_player.skill_executed.disconnect(_on_skill_executed)
	player_scene.mock_monster_invoked.disconnect(_on_mock_monster_invoked)


func _on_mock_monster_invoked(_player_scene: Control,count: int):
	_process_mock_monster(_player_scene,count)


func _on_war_stomp_triggered(_player_scene: Control, _effect: DataEffect):
	print("战争践踏！")
	# 特效相关的技能
	var effect_skill = _effect.get_special_skill()
	# 实例化战争践踏攻击区域场景
	var attack_zone_scene = SingletonGameScenePre.AttackEffect10Scene.instantiate()
	# 添加到场景树
	$CanvasLayer/GameZone/Effects.add_child(attack_zone_scene)
	# 开始攻击
	attack_zone_scene.start(effect_skill,_player_scene.position,_player_scene.scale.x)
	# 监听怪物检测
	attack_zone_scene.monster_detected.connect(_on_monster_detected)


func _on_skill_on_target_triggered(player_scene: Control, skill: DataBaseSkill,target: Control):
	print("技能指向怪物触发：",skill.name)
	_skill_executed_damage_judge([target],skill,player_scene)


func _on_find_target_started(player_scene: Control):
	#print('find_target_started:')
	var min_distance_monster = _find_min_distance_monster(player_scene)
	player_scene.set_attack_target(min_distance_monster)


func _on_find_far_target_started(player_scene: Control):
	var max_distance_monster = _find_max_distance_monster(player_scene)
	player_scene.set_attack_target(max_distance_monster)


func _on_attack_target_changed(attack_target):
	attack_target_changed.emit(attack_target)


func _find_min_distance_monster(player_scene: Control) -> Control:
	var min_distance = INF
	var min_distance_monster = null
	for monster in $CanvasLayer/GameZone/Monsters.get_children():
		if monster.data_monster.is_dead or monster.data_monster.is_black_monster:
			continue
		# 怪物和玩家距离
		var monster_distance = monster.global_position.distance_to(player_scene.global_position)
		# 记录最小距离
		if monster_distance < min_distance:
			min_distance = monster_distance
			min_distance_monster = monster
	return min_distance_monster


func _find_max_distance_monster(player_scene: Control) -> Control:
	var max_distance = 0
	var max_distance_monster = null
	for monster in $CanvasLayer/GameZone/Monsters.get_children():
		if monster.data_monster.is_dead or monster.data_monster.is_black_monster:
			continue
		var monster_distance = monster.global_position.distance_to(player_scene.global_position)
		if monster_distance > max_distance:
			max_distance = monster_distance
			max_distance_monster = monster
	return max_distance_monster


# 必须隐藏和展示CanvasLayer，根节点的show和hide无效
func scene_hide():
	$CanvasLayer.hide()


func scene_show():
	$CanvasLayer.show()


func _on_skill_executed(player: DataPlayer, skill: DataBaseSkill,skill_add_count: int = 0):
	if skill == null:
		return
	print("skill_executed:",player.player_id,skill.name)
	var player_scene = $CanvasLayer/GameZone/Players.get_node(player.player_id)
	if player_scene == null:
		return
	# 碰撞体区域类型技能判定
	if skill.id == "attack_add_sector_damage":
		_add_sector_damage_effect(player_scene.position,skill)
		# 临时技能方向，延时后直接用skill中的方向会异常
		var direction = skill.direction
		# 剑气纵横追加剑气
		if player.has_effect("effect_000034"):
			var effect2 = player.get_effect("effect_000034")
			if randf() < effect2.value:
				
				# 延迟0.3秒释放剑气
				await get_tree().create_timer(0.3).timeout
				skill.direction = direction
				_add_sector_damage_effect(player_scene.position,skill)
		## 狂风骤雨追加剑气
		if player.has_effect("effect_000058"):
			var effect2 = player.get_effect("effect_000058")
			if effect2.is_suit_invoked():
				for i in int(effect2.get_suit_value()):
					# 延迟0.2秒释放剑气
					await get_tree().create_timer(0.2).timeout
					skill.direction = direction
					_add_sector_damage_effect(player_scene.position,skill)
		return
	elif skill.id == "fire_light":
		# 爆炎射线
		_add_fire_light_damage_effect(player_scene.position,skill,player)
		return

	# 常规的中心点半径类型的技能伤害判定
	if skill.target_type == 1 or skill.target_type == 2:
		if skill.name == "华丽登场":
			pass
		var target_monster_list = _get_skill_effect_monster_list(player_scene, skill)

		if target_monster_list.size() > 0:
			# 获取首个目标的位置信息
			var first_target = target_monster_list[0]
			# 添加释放动画
			_skill_executed_casting(
				skill,
				player_scene,
				target_monster_list[0],
				skill_add_count,
				func(scene: Node2D):
					# 如果玩家离开了地图，则跳过
					if data_map.data_player == null:
						return
					# 解除和玩家的联系，加入到地图特效中
					_add_to_effect_groups(scene)

					var _target_monster_list = _get_skill_effect_monster_list(player_scene, skill)
					if _target_monster_list and _target_monster_list.size() > 0:
						# 添加技能名称动画
						_add_skill_name_ani(_target_monster_list[0],player_scene,skill)
						# 设置技能目标
						if scene:
							scene.set_target_scene(_target_monster_list[0]),
				func():
					# 重置玩家执行CD
					player.reset_execute_cd(),
				func():
					# 如果玩家离开了地图，则跳过
					if data_map.data_player == null:
						return
					# 开始伤害判定
					skill.damage_array_index = 0
					# 确定技能作用目标
					var _target_monster_list = _get_skill_effect_monster_list(player_scene, skill)

					# 记录是否有怪物死亡
					var is_monster_dead = false
					if _target_monster_list and _target_monster_list.size() > 0:
						# 伤害判定前记录下首个怪物的位置（判定后可能会销毁）
						var first_target_postion = _target_monster_list[0].position
						if first_target:
							first_target_postion = first_target.position

						is_monster_dead = _skill_executed_damage_judge(_target_monster_list,skill,player_scene)
						print("第0段技能伤害判定")
						# 如果技能追加判定为真，则执行追加技能
						if skill_add_count > 0:
							for i in range(skill_add_count):
								is_monster_dead = _skill_executed_damage_judge(_target_monster_list,skill,player_scene)

						# 如果技能伤害判定段数未结束，则启动剩余伤害判定定时器
						if not skill.is_damage_array_index_end():
							for i in range(1,skill.get_damage_array_size()):
								await get_tree().create_timer(skill.damage_interval/2).timeout
								is_monster_dead = _skill_executed_damage_judge(_target_monster_list,skill,player_scene)
								print("第",i,"段技能伤害判定")

						# 如果有怪物死亡，则判断瞬身追击
						if is_monster_dead:
							_judge_skill_monster_flash(player_scene,skill)
						
						# 释放后判断 烈火燎原
						if player_scene.attack_target:
							first_target_postion = player_scene.attack_target.position
						_judge_attack_attach_fire(player_scene,skill,first_target_postion)
			)


	elif skill.target_type == 0 or skill.target_type == 2:
		# 寻找技能作用目标
		var target_player_list = []

		# TODO 需要根据技能的作用范围，找到技能半径内的玩家
		for target_player in data_map.data_players.values():
			target_player_list.append(target_player)
		#print("执行技能：", skill.name, " 选取玩家目标：", target_player_list)

		# 技能作用到目标
		for target in target_player_list:
			data_map.player_buff_skill_effect(player, target, skill)	

# 巨人姿态攻击区域
func _add_gaint_attack_zone(player_scene,_skill: DataBaseSkill,_position: Vector2):
	var data_player = player_scene.data_player
	if data_player == null:
		return
	# 寻找一只最近的怪物
	var target_monster = _find_min_distance_monster(player_scene)
	var direction = player_scene.position.direction_to(target_monster.position).normalized()
	# 创建普攻和奋力一击的攻击区域
	var attack_effect_scene = SingletonGameScenePre.AttackEffect9Scene.instantiate()
	$CanvasLayer/GameZone/Effects.add_child(attack_effect_scene)
	if _skill.id == "skill_000000" or _skill.id == "skill_000001":
		attack_effect_scene.start_stick(_position,_skill,direction)
	elif _skill.id == "skill_000002":
		attack_effect_scene.start_swipe(_position,_skill,direction)
	# 监听攻击区域怪物检测
	attack_effect_scene.monster_detected.connect(_on_monster_detected)
		
		

func _judge_attack_attach_fire(player_scene,skill: DataBaseSkill,_position: Vector2):
	var data_player = player_scene.data_player
	if data_player == null:
		return
	if data_player \
		# 法力爆裂 才会触发
		and skill.id == "skill_000101" \
		and data_player.has_effect("effect_000050"):

		var effect = data_player.get_effect("effect_000050")
		if data_player.mp < effect.mp_cost:
			return
		# 消耗法力
		data_player.recover_mp(-effect.mp_cost)
		# 区域大小缩放比例计算
		var skill_radius_enhance = 0
		if data_player.has_skill_enhance(skill.id):
			var skill_enhance = player_scene.data_player.get_skill_enhance(skill.id)
			skill_radius_enhance = skill_enhance.radius
		var zone_size = (skill.radius + skill_radius_enhance) / float(skill.radius)
		# 生成一个火焰区域
		_add_fire_zone_damage_effect(effect,data_player,_position,zone_size)


func _add_fire_zone_damage_effect(
	effect: DataEffect,
	_data_player: DataPlayer,
	_position: Vector2,
	_scale: float
	):
	if _data_player == null:
		return
	var file_zone_scene = SingletonGameScenePre.AttackEffect8Scene.instantiate()
	# “灼烧”效果伤害值
	var damage_value = _data_player.get_final_details().magic * effect.value
	# “灼烧”间隔时间减少（秒）
	var interval_reduce = 0.0
	if _data_player.has_effect("effect_000053"):
		var effect_fire_more_frequency = _data_player.get_effect("effect_000053")
		interval_reduce += effect_fire_more_frequency.value
	# 炎灭八荒 间隔时间减少
	if _data_player.has_effect("effect_000061"):
		var effect_fire_more_frequency = _data_player.get_effect("effect_000061")
		if effect_fire_more_frequency.is_suit_invoked():
			var effect_value = effect_fire_more_frequency.value / effect_fire_more_frequency.suit_num
			interval_reduce += (1 - interval_reduce) * effect_value
	# 设定间隔最小值
	interval_reduce = min(0.95,interval_reduce)
	# “灼烧”buff的id，需要保证唯一，否则会覆盖，导致旧buff删除，引发问题
	var buff_id = effect.id + "_" + str(Time.get_ticks_msec())
	# 生成“灼烧”buff信息
	var buff = _create_fire_buff(buff_id,damage_value,interval_reduce)
	file_zone_scene.start(buff,_position,_scale)
	$CanvasLayer/GameZone/Effects.add_child(file_zone_scene)


func _create_fire_buff(buff_id: String,value: int,interval_reduce: float) -> DataBuff:
	# “灼烧”效果
	var fire_effect = DataEffect.new("effect_000051", "on_fire")
	fire_effect.value = value
	fire_effect.not_append_value = value
	fire_effect.invoke_interval = 1000 * (1 - interval_reduce)
	# “灼烧”buff持续永久持续，直到区域消失
	var buff = DataBuff.new()
	buff.id = buff_id
	buff.name = "灼烧"
	buff.add_effect(fire_effect)
	return buff


func _judge_skill_monster_flash(player_scene,skill: DataBaseSkill):
	if data_map.data_player \
		# 普攻或者技能触发
		and (skill.id == "skill_000000" or skill.id == "skill_000201") \
		# 瞬身追击
		and data_map.data_player.has_effect("effect_000036"):
		var effect = data_map.data_player.get_effect("effect_000036")
		var hit_rate = effect.value
		# 概率加成
		hit_rate += data_map.luck_rate_add()
		if randf() < hit_rate:
			# 寻找距离最远的怪物，并移动到怪物坐标位置
			var max_distance_monster = _find_max_distance_monster(player_scene)
			if max_distance_monster:
				# 瞬移到目标位置
				player_scene.flash_to_target(max_distance_monster)
		else:
			print("未击中")
			


# 解除链接并加入地图特效层（防止技能弹道脱手后仍然跟随玩家移动）
func _add_to_effect_groups(scene: Node2D):
	if not scene:
		return
	var global_pos = scene.global_position
	var parent = scene.get_parent()
	if parent:
		parent.remove_child(scene)
		$CanvasLayer/GameZone/Effects.add_child(scene)
		scene.global_position = global_pos


# 统计技能作用半径内的怪物
func _get_skill_effect_monster_list(player_scene: Control, skill: DataBaseSkill) -> Array:
	var effect_position = skill.effect_position
	if effect_position == null:
		# 如果技能中心点类型为0，则使用玩家位置
		if skill.center_type == 0:
			effect_position = player_scene.global_position
		# 如果技能中心点类型为1，则使用目标位置作为中心位置
		else:
			if player_scene.attack_target:
				effect_position = player_scene.attack_target.global_position
			else:
				return []

	# 技能半径
	var radius = skill.radius
	# 技能目标数量
	var count = skill.count
	# 特殊效果增幅
	if player_scene.data_player.has_skill_enhance(skill.id):
		var skill_enhance = player_scene.data_player.get_skill_enhance(skill.id)
		radius += skill_enhance.radius
		count += skill_enhance.count
	# “毫无章法”技能范围和数量增幅
	if skill.id in ["skill_000000","skill_000201"]:
		if player_scene.data_player.has_effect("effect_000037"):
			var effect = player_scene.data_player.get_effect("effect_000037")
			if effect.value_type == Constants.VALUE_TYPE_NUMBER:
				radius += effect.value
				count += effect.level
	# 其他增幅
	radius *= (1 + player_scene.data_player.attack_range_increase)

	# 统计技能半径范围内的怪物
	var temp_monster_list = []
	for monster_scene in $CanvasLayer/GameZone/Monsters.get_children():
		if monster_scene.data_monster.is_dead or monster_scene.data_monster.is_black_monster:
			continue
		var monster_distance = monster_scene.global_position.distance_to(effect_position)
		#print("monster_distance:",monster_distance)
		# 怪物在技能范围内（1为帧率等误差）
		if monster_distance <= (radius + 1):
			temp_monster_list.append({"monster": monster_scene, "distance": monster_distance})
	
	# 按距离从小到大排序
	temp_monster_list.sort_custom(func(a, b):
		# 优先选中玩家锁定的目标
		if a['monster'] == player_scene.attack_target:
			return true
		if b['monster'] == player_scene.attack_target:
			return false
		return a.distance < b.distance
	)
		
	# 取前skill.count个最近的怪物
	var target_monster_list = []
	for i in range(min(count, temp_monster_list.size())):
		target_monster_list.append(temp_monster_list[i].monster)
	return target_monster_list


# 技能执行后，正在施放阶段
func _skill_executed_casting(
	skill: DataBaseSkill,
	player_scene: Control,
	first_target: Control,
	skill_add_count: int,
	call_back_cast_finished: Callable,
	call_back_cast_interrupted: Callable,
	call_back_finished: Callable,
):
	if EFFECT_SKILLS.has(skill.id):
		if player_scene.data_player.is_resting:
			return
		var attack_effect = SingletonGameScenePre.AttackEffect2Scene.instantiate()
		attack_effect.skill_id = skill.id
		attack_effect.skill_radius = skill.radius
		attack_effect.skill_add_count = skill_add_count
		player_scene.add_child(attack_effect)
		attack_effect.set_target_scene(first_target)
		attack_effect.global_position = player_scene.global_position
		attack_effect.move_speed = skill.skill_move_speed
		attack_effect.scale_duration = skill.cd
		# 黑魔法技能颜色
		if player_scene.data_player.has_effect("effect_000045"):
			attack_effect.set_black_color()
		# 爆炎术士技能颜色
		elif player_scene.data_player.job_id == "job_000103":
			attack_effect.set_orange_color()
		# 技能增幅
		if player_scene.data_player.has_skill_enhance(skill.id):
			var skill_enhance = player_scene.data_player.get_skill_enhance(skill.id)
			attack_effect.scale_duration = skill.cd + skill_enhance.cd
			attack_effect.skill_radius = skill.radius + skill_enhance.radius
		# 监听动画释放完毕
		attack_effect.ani_skill_cast_finished.connect(call_back_cast_finished)
		# 监听动画被打断，动画打断则重置玩家执行CD
		attack_effect.ani_skill_cast_interrupted.connect(call_back_cast_interrupted)
		# 监听动画结束
		attack_effect.ani_all_finished.connect(call_back_finished)
	else:
		if skill.id == "attack_explode_damage":
			# 添加爆炸粒子特效
			_add_explode_particle_effect(first_target.position)
		elif skill.id == "meteor_fall":
			# 添加流星陨落特效
			_add_meteor_fall_effect(skill.effect_position,call_back_cast_finished,call_back_finished)
			# 流星陨落有自己的回调时机选择
			return
		# 没有释放动画的直接执行回调
		call_back_cast_finished.call(null)
		call_back_finished.call()


# 伤害判定
func _skill_executed_damage_judge(_target_monster_list, skill: DataBaseSkill, player_scene) -> bool:
	# 判断是否有怪物因为技能死亡
	var is_monster_dead = false

	for monster_scene in _target_monster_list:
		# 计算攻击方向
		var direction = (monster_scene.global_position - player_scene.global_position).normalized()
		# 伤害判定
		data_map.player_attack_skill_effect(
			player_scene.data_player, 
			monster_scene.data_monster, 
			skill, 
			direction,
			monster_scene.global_position,
			_target_monster_list.size())
		# 如果怪物未死亡，则设定目标为攻击者
		if not monster_scene.data_monster.is_dead and not player_scene.data_player.is_copy:
			monster_scene.set_attack_target(player_scene)
		else:
			is_monster_dead = true

	return is_monster_dead


func _add_skill_name_ani(first_target,player_scene,skill):
	# 添加释放特效
	var direction = (first_target.global_position - player_scene.global_position).normalized()
	player_scene.add_skill_executed_effect(skill,direction)


func _add_meteor_fall_effect(_position: Vector2,call_back_cast_finished: Callable,call_back_finished: Callable):
	var meteor_fall_effect = SingletonGameScenePre.MeteorFallEffectScene.instantiate()
	$CanvasLayer/GameZone/Effects.add_child(meteor_fall_effect)
	meteor_fall_effect.play_effect(_position)
	meteor_fall_effect.finished.connect(func():
		call_back_cast_finished.call(null)
		call_back_finished.call()
	)


func _add_explode_particle_effect(_position: Vector2):
	var explode_particle_effect = SingletonGameScenePre.AttackEffectScene.instantiate()
	explode_particle_effect.position = _position
	$CanvasLayer/GameZone/Effects.add_child(explode_particle_effect)
	explode_particle_effect.play_effect()


func _add_fire_light_damage_effect(_position: Vector2, _skill: DataBaseSkill,_player: DataPlayer):
	if _player == null:
		return
	var fire_damage_effect = SingletonGameScenePre.AttackEffect7Scene.instantiate()
	# 突破天际大小增幅
	var scale_x = 1.0
	if _player.has_effect("effect_000052"):
		var effect = _player.get_effect("effect_000052")
		scale_x += effect.value
	fire_damage_effect.start(_skill,_position,_skill.direction,scale_x)
	$CanvasLayer/GameZone/Effects.add_child(fire_damage_effect)
	# 监听怪物检测
	fire_damage_effect.monster_detected.connect(_on_monster_detected)


func _add_sector_damage_effect(_position: Vector2, _skill: DataBaseSkill):
	print("破空剑气：添加场景",_skill.direction)
	var sector_damage_effect = SingletonGameScenePre.AttackEffect5Scene.instantiate()
	sector_damage_effect.start(_skill,_position,_skill.direction)
	$CanvasLayer/GameZone/Effects.add_child(sector_damage_effect)
	# 监听怪物检测
	sector_damage_effect.monster_detected.connect(_on_monster_detected)


func _on_monster_detected(monster: Monster,skill: DataBaseSkill):
	#print("monster_detected:",monster.name,skill.name)
	if data_map.data_player:
		var player_scene = $CanvasLayer/GameZone/Players.get_node(data_map.data_player.player_id)
		if player_scene:
			_skill_executed_damage_judge([monster],skill,player_scene)
						

func _on_drag_monster_requested(_player: DataPlayer,_speed: int):
	print("drag_monster_requested:",_player.player_id)
	var _player_scene = $CanvasLayer/GameZone/Players.get_node(_player.player_id)
	# 将当前地图所有怪物吸引至目标位置30码
	for monster_scene in $CanvasLayer/GameZone/Monsters.get_children():
		if monster_scene.data_monster.is_dead:
			continue


func _on_map_drop_bt_pressed() -> void:
	map_drop_showed.emit(data_map)


func _on_endless_exit():
	endless_exit.emit()


func _on_drop_dic_bt_pressed() -> void:
	drop_dic_showed.emit(data_map)


func _on_get_endless_reward():
	data_map.get_endless_reward()
	# 增加提示
	ToastManager.add_toast("获得2点能力值")

		
